#include "LecteurPhraseAvecArbre.h"

#include <stdlib.h>
#include <iostream>
using namespace std;

////////////////////////////////////////////////////////////////////////////////
LecteurPhraseAvecArbre::LecteurPhraseAvecArbre(string nomFich) :
	ls(nomFich), ts() {
    correct = true;
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::analyse() {
	arbre=programme();
    if(correct)
        cout << "Syntaxe correcte." << endl;
    else {
        cout << "Syntaxe incorrecte." << endl;
        exit(0);
    }
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::programme() {
	// <programme> ::= debut <seq_inst> fin FIN_FICHIER
    Noeud* si = NULL;
	sauterSymCour("debut");
    try {
        si = seqInst();
    } catch (ExceptionSyntaxe i) {
        erreurSyntaxe(i);
    }
	sauterSymCour("fin");
    try {
        testerSymCour("<FINDEFICHIER>");
    } catch(ExceptionSymbole i) {
        erreurSymbole(i);
    }
	return si;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::seqInst() {
	// <seqInst> ::= <inst>  { <inst>  }

	NoeudSeqInst* si = new NoeudSeqInst();
	do {
        try {
            si->ajouteInstruction(inst());
        } catch(ExceptionSyntaxe i) {
            erreurSyntaxe(i);
        }
	} while (ls.getSymCour()=="<VARIABLE>" || ls.getSymCour() == "si" ||
            ls.getSymCour() == "tantque" || ls.getSymCour() == "repeter" ||
            ls.getSymCour() == "ecrire" || ls.getSymCour() == "lire" ||
            ls.getSymCour() == "pour");
	// tant que le symbole courant est un debut possible d'instruction...
	return si;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::inst() {
// <inst> ::= <affectation> | <instSi> | <instTq> | <instRepeter> | <instEcrire>
//            | <instLire> | <instPour>

	if (ls.getSymCour() == "si") {
        return instSi();
    } else if (ls.getSymCour() == "tantque") {
        return instTq();
    } else if (ls.getSymCour() == "repeter") {
        return instRepeter();
    } else if (ls.getSymCour() == "pour") {
        return instPour();
    } else if (ls.getSymCour() == "ecrire") {
        return instEcrire();
    } else if (ls.getSymCour() == "lire") {
        return instLire();
    } else {
        return affectation();
    }
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::affectation() {
// <affectation> ::= <variable> = <expression> ;

    Noeud* exp = NULL;;
    try {
        testerSymCour("<VARIABLE>");
    } catch (ExceptionSymbole i) {
        erreurSymbole(i);
    }
	Noeud* var = ts.chercheAjoute(ls.getSymCour());
	ls.suivant();
	sauterSymCour("=");
    try {
        exp = expression();
    } catch (ExceptionSyntaxe i) {
        erreurSyntaxe(i);
    }
    sauterSymCour(";");
	return new NoeudAffectation(var,exp);
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instSi() 
// <instSi> ::= si ( <expBool> ) <seqInst> { sinonsi ( <expBool> ) <seqInst> }
//              [ sinon <seqInst> ] finsi
{
    Noeud* expBoo = NULL;
    Noeud* seqIns = NULL;
    sauterSymCour("si");
    sauterSymCour("(");
    try {
        expBoo = expBool();
    } catch (ExceptionSyntaxe i) {
        erreurSyntaxe(i);
    }
    sauterSymCour(")");
    try {
        seqIns = seqInst();
    } catch (ExceptionSyntaxe i) {
        erreurSyntaxe(i);
    }
    NoeudInstSi* head = new NoeudInstSi(expBoo,seqIns,NULL);
    NoeudInstSi* courant = head;
    while (ls.getSymCour() == "sinonsi") {
        ls.suivant();
        sauterSymCour("(");
        try {
            expBoo = expBool();
        } catch (ExceptionSyntaxe i) {
            erreurSyntaxe(i);
        }
        sauterSymCour(")");
        try {
            seqIns = seqInst();
        } catch (ExceptionSyntaxe i) {
            erreurSyntaxe(i);
        }
        NoeudInstSi* is = new NoeudInstSi(expBoo,seqIns,NULL);
        courant->ajouteSinon(is);
        courant = is;
    }
    if (ls.getSymCour() == "sinon") {
        ls.suivant();
        try {
        courant->ajouteSinon(seqInst());
        } catch (ExceptionSyntaxe i) {
            erreurSyntaxe(i);
        }
    }
    sauterSymCour("finsi");
    return head;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instTq() 
// <instTq> ::= tantque ( <expBool> ) <seqInst> fintantque
{
    Noeud* expressBool = NULL;
    Noeud* sequenceInst = NULL;
    sauterSymCour("tantque");
    sauterSymCour("(");
    try {
        expressBool= expBool();
    } catch (ExceptionSyntaxe i) {
        erreurSyntaxe(i);
    }
    sauterSymCour(")");
    try {
        sequenceInst = seqInst();
    } catch (ExceptionSyntaxe i) {
        erreurSyntaxe(i);
    }
    sauterSymCour("fintantque");
    return new NoeudInstTq(expressBool,sequenceInst);
}
////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instRepeter()
// <instRepeter> ::= repeter <seqInst> jusqua ( <expBool> )
{
    Noeud* sequenceInst = NULL;
    Noeud* expressBool = NULL;
    sauterSymCour("repeter");
    try {
        sequenceInst = seqInst();
    } catch (ExceptionSyntaxe i) {
        erreurSyntaxe(i);
    }
    sauterSymCour("jusqua");
    sauterSymCour("(");
    try {
        expressBool= expBool();
    } catch (ExceptionSyntaxe i) {
        erreurSyntaxe(i);
    }
    sauterSymCour(")");
    return new NoeudInstRepeter(expressBool,sequenceInst);
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instPour() 
// <instPour> ::= pour ( <affectation> <expBool> ; <affectation>) <seqInst> finpour
{
    sauterSymCour("pour");
    sauterSymCour("(");
    NoeudInstPour* pour = new NoeudInstPour();
    try {
        pour->setInitialisation(affectation());
    } catch (ExceptionSyntaxe i) {
        erreurSyntaxe(i);
    }
    try {
        pour->setCondition(expBool());
    } catch (ExceptionSyntaxe i) {
        erreurSyntaxe(i);
    }
    sauterSymCour(";");
    try {
        pour->setOperation(affectation());
    } catch (ExceptionSyntaxe i) {
        erreurSyntaxe(i);
    }
    sauterSymCour(")");
    try {
        pour->setSeqInst(seqInst());
    } catch (ExceptionSyntaxe i) {
        erreurSyntaxe(i);
    }
    sauterSymCour("finpour");
    
    return pour;
}
////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instEcrire() {
// <instEcrire> ::= ecrire ( <expression> | <chaine> ) ;
    sauterSymCour("ecrire");
    sauterSymCour("(");
    NoeudInstEcrire* ecrire = new NoeudInstEcrire();
    if(ls.getSymCour() == "<CHAINE>") {
        ecrire->ajouteChaine(ls.getSymCour().getChaine());
        ls.suivant();
    }
    else {
        try {
            ecrire->ajouteExpression(expression());
        } catch (ExceptionSyntaxe i) {
            erreurSyntaxe(i);
        }
    }
    sauterSymCour(")");
    sauterSymCour(";");
   
    return ecrire;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instLire() {
// <instLire> ::= lire ( <variable> ) ;
    sauterSymCour("lire");
    sauterSymCour("(");
    Noeud* var = ts.chercheAjoute(ls.getSymCour());
    NoeudInstLire* lire = new NoeudInstLire(var);
    ls.suivant();
    sauterSymCour(")");
    sauterSymCour(";");
    return lire;
}
////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::expression() {
// <expression> ::= <terme> { <opAdd> <terme> }
    
    Noeud* fact = NULL;
    Symbole operateur;
    Noeud* factDroit = NULL;
    try {    
        fact = terme();
    } catch (ExceptionSyntaxe i) {
        erreurSyntaxe(i);
    }
	while (ls.getSymCour()=="+" || ls.getSymCour()=="-") {
		try {
            operateur = opAdd(); // on stocke le symbole de l'opérateur
        } catch (ExceptionSyntaxe i) {
            erreurSyntaxe(i);
        }
        try {
            factDroit=terme(); // lecture de l'operande droit
        } catch (ExceptionSyntaxe i) {
            erreurSyntaxe(i);
        }
		fact = new NoeudOperateurBinaire(operateur,fact,factDroit); // const. du noeud
	}
	return fact;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::terme() {
// <terme> ::= <facteur> { <opMult> facteur> }
    
    Noeud* fact = NULL;
    Symbole operateur;
    Noeud* factDroit = NULL;
    try {
        fact = facteur();
    } catch (ExceptionSyntaxe i) {
        erreurSyntaxe(i);
    }
	while (ls.getSymCour()=="*" || ls.getSymCour()=="/") {
        try {
            operateur = opMult(); // on stocke le symbole de l'opérateur
        } catch (ExceptionSyntaxe i) {
            erreurSyntaxe(i);
        }
        try {
            factDroit=facteur(); // lecture de l'operande droit
        } catch (ExceptionSyntaxe i) {
            erreurSyntaxe(i);
        }
		fact = new NoeudOperateurBinaire(operateur,fact,factDroit); // const. du noeud
	}
	return fact;
}
////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::facteur() {
//<facteur> ::= <entier> | <variable> | <opUnaire> <expBool> | ( <expBool> )

	Noeud* fact=NULL;

	if (ls.getSymCour()=="<VARIABLE>" || ls.getSymCour()=="<ENTIER>") {
		fact=ts.chercheAjoute(ls.getSymCour());
		ls.suivant();
	} else if (ls.getSymCour()=="-" || ls.getSymCour() == "non") {
		ls.suivant();
		// on représente le moins unaire (- facteur) par une soustraction binaire (0 - facteur)
        try {
            fact = new NoeudOperateurBinaire(Symbole("-"), ts.chercheAjoute(Symbole("0")), facteur());
        } catch (ExceptionSyntaxe i) {
            erreurSyntaxe(i);
        }
	} else if (ls.getSymCour()=="(") {
		ls.suivant();
        try {
            fact=expression();
        } catch (ExceptionSyntaxe i) {
            erreurSyntaxe(i);
        }
		sauterSymCour(")");
	} else
		throw ExceptionSyntaxe("<facteur>",ls.getSymCour().getChaine());
	return fact;
}

////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opAdd() {
// <opAdd> ::= + | -
    Symbole operateur;
	if (ls.getSymCour()=="+" || ls.getSymCour()=="-") {
		operateur=ls.getSymCour();
		ls.suivant();
	}
	else
        throw ExceptionSyntaxe("<opAdd>",ls.getSymCour().getChaine());
	return operateur;
}

////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opMult() {
// <opMult> ::= * | /    
    Symbole operateur;
	if (ls.getSymCour()=="*" || ls.getSymCour()=="/") {
		operateur=ls.getSymCour();
		ls.suivant();
	}
	else
        throw ExceptionSyntaxe("<opMult>",ls.getSymCour().getChaine());
	return operateur;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::expBool() {
//<expBool> ::= <lien> { <opOu> <lien> }   
    Noeud* fact = NULL;
    Symbole operateur;
    Noeud* factDroit = NULL;
    try {
        fact = lien();
    } catch (ExceptionSyntaxe i) {
        erreurSyntaxe(i);
    }
	while (ls.getSymCour()=="ou") {
        try {
            operateur = opOu(); // on stocke le symbole de l'opérateur
        } catch (ExceptionSyntaxe i) {
            erreurSyntaxe(i);
        }
        try {
            factDroit=lien(); // lecture de l'operande droit
        } catch (ExceptionSyntaxe i) {
            erreurSyntaxe(i);
        }
		fact = new NoeudOperateurBinaire(operateur,fact,factDroit); // const. du noeud
	}
	return fact;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::lien() {
//<lien> ::= <relation> { <opEt> <relation> }
    Noeud* fact = NULL;
    Symbole operateur;
    Noeud* factDroit = NULL;
    try {
        fact = relation();
    } catch (ExceptionSyntaxe i) {
        erreurSyntaxe(i);
    }
	while (ls.getSymCour()=="et") {
        try {
            operateur = opEt(); // on stocke le symbole de l'opérateur
        } catch (ExceptionSyntaxe i) {
            erreurSyntaxe(i);
        }
        try {
            factDroit=relation(); // lecture de l'operande droit
        } catch (ExceptionSyntaxe i) {
            erreurSyntaxe(i);
        }
		fact = new NoeudOperateurBinaire(operateur,fact,factDroit); // const. du noeud
	}
	return fact;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::relation() {
//<relation> ::= <expression> { <opRel> <expression> }    
    Noeud* fact = NULL;
    Symbole operateur;
    Noeud* factDroit = NULL;
    try {
        fact = expression();
    } catch (ExceptionSyntaxe i) {
        erreurSyntaxe(i);
    }
	while (ls.getSymCour() == "==" || ls.getSymCour() == "!=" ||
            ls.getSymCour() == ">" || ls.getSymCour() == ">=" ||
            ls.getSymCour() == "<" || ls.getSymCour() == "<=") {
        try {
            operateur = opRel(); // on stocke le symbole de l'opérateur
        } catch (ExceptionSyntaxe i) {
            erreurSyntaxe(i);
        }
        try {
            factDroit=expression(); // lecture de l'operande droit
        } catch (ExceptionSyntaxe i) {
            erreurSyntaxe(i);
        }
		fact = new NoeudOperateurBinaire(operateur,fact,factDroit); // const. du noeud
	}
	return fact;
}

////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opRel() {
//<opRel> ::= == | != | < | <= | > | >=    
    Symbole operateur;
	if (ls.getSymCour() == "==" || ls.getSymCour() == "!=" ||
            ls.getSymCour() == ">" || ls.getSymCour() == ">=" ||
            ls.getSymCour() == "<" || ls.getSymCour() == "<=") {
		operateur=ls.getSymCour();
		ls.suivant();
	}
	else
		throw ExceptionSyntaxe("<opRel>",ls.getSymCour().getChaine());
	return operateur;
}

////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opOu() {
//<opOu> ::= ou
    Symbole operateur;
	if (ls.getSymCour() == "ou") {
		operateur=ls.getSymCour();
		ls.suivant();
	}
	else
		throw ExceptionSyntaxe("<opOu>",ls.getSymCour().getChaine());
	return operateur;
}

Symbole LecteurPhraseAvecArbre::opEt() {
//<opOu> ::= et
    Symbole operateur;
	if (ls.getSymCour() == "et") {
		operateur=ls.getSymCour();
		ls.suivant();
	}
	else
		throw ExceptionSyntaxe("<opEt>", ls.getSymCour().getChaine());
	return operateur;    
    
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::testerSymCour(string ch) {
	if (ls.getSymCour() != ch) {
		throw ExceptionSymbole(ch,ls.getSymCour().getChaine());
	}
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::sauterSymCour(string ch) {
    try {
        testerSymCour(ch);
        ls.suivant();
    } catch (ExceptionSymbole i) {
        erreurSymbole(i);
    }
    
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::erreurSyntaxe(ExceptionSyntaxe i) {
	cout << endl << "-------- Erreur ligne " << ls.getLigne() << " - Colonne "
			<< ls.getColonne() << endl << "   Attendu : " << i.getAttendu() << endl
			<< "   Trouve  : " << i.getTrouve() << endl << endl;
    correct = false;
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::erreurSymbole(ExceptionSymbole i) {
    cout << endl << "-------- Erreur ligne " << ls.getLigne() << " - Colonne "
			<< ls.getColonne() << endl << "   Attendu : " << i.getAttendu() << endl
			<< "   Trouve  : " << i.getTrouve() << endl << endl;
    correct = false;
}
